#include "gkt_board.h"
#include "gkt_debug.h"
#include "gkt_os.h"
#include "gkt_screen.h"
#include "gkt_card.h"
#include "gkt_door.h"
#include "gkt_io_debounce.h"
#include "gkt_factory_reset.h"
#include "gkt_sliding_closure.h"
#include "gkt_window.h"
#include "gkt_network.h"
#include "gkt_cat_eye.h"
#include "gkt_indoor_bell.h"
#include "gkt_backboard.h"
#include "gkt_visual_intercom.h"

#define WINDOW_SYSTEM_TIMER_MASK	\
	(GKT_WINDOW_TIMER_MANAGE | (GKT_WINDOW_TIMER_MANAGE_SYSTEM << GKT_WINDOW_TIMER_SUB_GROUP_SHIFT))

typedef struct _window_system {
	int	standby_disable;
	int	standby_pending;

#if GKT_SLIDING_CLOSURE_ENABLE
	int	sliding_closure_detect;
	uint32_t	sliding_closure_seq;
	int	card_is_masked;
#endif

	uint32_t	lock_status;
	uint32_t	timer_active_bits;

	uint32_t	pending_counter;
	uint32_t	pending_event_bits;
	uint32_t	pending_event_data[GKT_WINDOW_SYSTEM_MAX][GKT_EVENT_DATA_WORDS];
	uint32_t	pending_event_counter[GKT_WINDOW_SYSTEM_MAX];
} window_system_s;
static window_system_s s_window_system;

#if defined(GKT_CONFIG_DOOR_LOCK_ENABLE) && GKT_CONFIG_DOOR_LOCK_ENABLE	\
	|| (defined(GKT_CONFIG_FACTORY_RESET_ENABLE) && GKT_CONFIG_FACTORY_RESET_ENABLE \
	&& GKT_FACTORY_RESET_INDIVIDUAL_ENABLE)	\
	|| (defined(GKT_CONFIG_CAT_EYE_ENABLE) && GKT_CONFIG_CAT_EYE_ENABLE)	\
	|| (defined(GKT_CONFIG_INDOOR_BELL_ENABLE) && GKT_CONFIG_INDOOR_BELL_ENABLE)	\
	|| (defined(GKT_CONFIG_BACKBOARD_ENABLE) && GKT_CONFIG_BACKBOARD_ENABLE)	\
	|| (defined(GKT_CONFIG_NETWORK_ENABLE) && GKT_CONFIG_NETWORK_ENABLE)	\
	|| GKT_SLIDING_CLOSURE_ENABLE
static void window_system_pend_event(uint32_t minor_type, 
				uint32_t data[GKT_EVENT_DATA_WORDS])
{
	uint32_t i;

	s_window_system.pending_event_bits |= 1U << minor_type;
	for (i = 0; i < GKT_EVENT_DATA_WORDS; i++)
		s_window_system.pending_event_data[minor_type][i] = data[i];
	s_window_system.pending_event_counter[minor_type] = s_window_system.pending_counter;
	s_window_system.pending_counter++;	
}
#endif

void gkt_window_system_pending_process(void)
{
	gkt_event_s event;
	uint32_t minor_type, minor_type_prior, index_min, i;
	int retval;

	while (s_window_system.pending_event_bits) {
		index_min = ~0;
		minor_type_prior = GKT_WINDOW_SYSTEM_MAX;

		for (minor_type = 0; minor_type < GKT_WINDOW_SYSTEM_MAX; minor_type++) {
			if (s_window_system.pending_event_bits & (1U << minor_type)) {
				if (s_window_system.pending_event_counter[minor_type] < index_min) {
					index_min = s_window_system.pending_event_counter[minor_type];
					minor_type_prior = minor_type;
				}
			}
		}

		if (minor_type_prior < GKT_WINDOW_SYSTEM_MAX) {
			event.flags = 0;
			event.major_type = GKT_EVENT_SYSTEM;
			event.minor_type = minor_type_prior;
			for (i = 0; i < GKT_EVENT_DATA_WORDS; i++)
				event.data[i] = (uint32_t)s_window_system.pending_event_data[minor_type_prior][i];
			retval = gkt_event_put(&event, 0);
			if (GKT_SUCCESS == retval) {
				s_window_system.pending_event_bits &= ~(1U << minor_type_prior);
				continue;
			}
		}

		break;
	}
}

#if GKT_SLIDING_CLOSURE_ENABLE
static void window_system_stop_timer(uint32_t type)
{
	uint32_t mask;

	mask = 1U << type;
	if (s_window_system.timer_active_bits & mask) {
		s_window_system.timer_active_bits &= ~mask;
		gkt_window_timer_clear(WINDOW_SYSTEM_TIMER_MASK | type);
	}
}
static int window_system_start_timer(uint32_t type, 
				uint32_t interval, int periodic, 
				uint32_t private_data)
{
	uint32_t mask, flags;
	int retval;

	window_system_stop_timer(type);

	mask = 1U << type;
	if (!(s_window_system.timer_active_bits & mask)) {
		flags = periodic ? GKT_WINDOW_TIMER_F_PERIODIC : 0;
		retval = gkt_window_timer_set(WINDOW_SYSTEM_TIMER_MASK | type,
						interval, flags, private_data);
		if (GKT_SUCCESS == retval)
			s_window_system.timer_active_bits |= mask;
	}

	return GKT_SUCCESS;
}
#endif

/************************************************************
 * DOOR ALWAYS OPEN
 ************************************************************/
#if defined(GKT_CONFIG_WINDOW_ALWAYS_OPEN_MODE_ENABLE) \
	&& GKT_CONFIG_WINDOW_ALWAYS_OPEN_MODE_ENABLE
static int s_window_door_always_open_state = 0;
void gkt_window_system_door_set_always_open_state(int state)
{
	s_window_door_always_open_state = state;
}
#else
#define s_window_door_always_open_state	0
#endif

void gkt_window_system_standby_enable(void)
{
	s_window_system.standby_disable = 0;
	gkt_info("window_system: standby enable.\n");
}
void gkt_window_system_standby_disable(void)
{
	s_window_system.standby_disable = 1;
	gkt_info("window_system: standby disable.\n");
}

void gkt_window_system_standby(int force)
{
	gkt_window_s *window;

	gkt_unused(window);

	gkt_info("window_system: standby - disable(%d), pending(%d)\n",
					s_window_system.standby_disable, 
					s_window_system.standby_pending);
	if (s_window_system.standby_disable && !force) {
		s_window_system.standby_pending = 1;
		return;
	}

	if (!s_window_door_always_open_state) {
#if (defined(GKT_CONFIG_DOOR_LOCK_ENABLE) && GKT_CONFIG_DOOR_LOCK_ENABLE)\
		&&(!defined(GKT_CONFIG_DOOR_LOCK_AUTOMATIC) || !GKT_CONFIG_DOOR_LOCK_AUTOMATIC)
		uint32_t lock_status = gkt_door_lock_get_status();
		if (lock_status != GKT_DOOR_LOCK_ST_CLOSED) {
			gkt_info("window_system: [STANDBY] lock status = %u\n", lock_status);

			if (!force) {
				s_window_system.standby_pending = 1;
				return;
			}
			else {
				gkt_door_lock_close(0);
				gkt_thread_sleep(1000);
			}
		}
#endif

#if defined(GKT_CONFIG_DOOR_MAGNETIC_ENABLE) && GKT_CONFIG_DOOR_MAGNETIC_ENABLE
		int magnetic_detected = gkt_door_magnetic_is_detected()
		if (!magnetic_detected) {
			window = gkt_window_find(GKT_WINDOW_ID_START);
			gkt_window_custom_event_handle(window,
				GKT_EVENT_SYSTEM, GKT_WINDOW_SYSTEM_DOOR,
				GKT_DOOR_ST_RISKY, 0);
		}
#endif
	}

#if defined(GKT_CONFIG_BACKBOARD_ENABLE) && GKT_CONFIG_BACKBOARD_ENABLE
	gkt_backboard_notify_frontboard_standby_status();
	gkt_thread_sleep(10);
#endif

	gkt_system_low_power(GKT_SYSTEM_LP_STANDBY);
}
#if defined(GKT_CONFIG_DOOR_LOCK_ENABLE) && GKT_CONFIG_DOOR_LOCK_ENABLE	\
	|| (defined(GKT_CONFIG_FACTORY_RESET_ENABLE) && GKT_CONFIG_FACTORY_RESET_ENABLE \
	&& GKT_FACTORY_RESET_INDIVIDUAL_ENABLE)	\
	|| (defined(GKT_CONFIG_CAT_EYE_ENABLE) && GKT_CONFIG_CAT_EYE_ENABLE)	\
	|| (defined(GKT_CONFIG_INDOOR_BELL_ENABLE) && GKT_CONFIG_INDOOR_BELL_ENABLE)	\
	|| (defined(GKT_CONFIG_BACKBOARD_ENABLE) && GKT_CONFIG_BACKBOARD_ENABLE)	\
	|| (defined(GKT_CONFIG_NETWORK_ENABLE) && GKT_CONFIG_NETWORK_ENABLE)	\
	|| GKT_SLIDING_CLOSURE_ENABLE
static void window_system_put_event(uint32_t minor_type, 
				uint32_t data[GKT_EVENT_DATA_WORDS])
{
	gkt_event_s event;
	uint32_t timeout, i;
	int in_irq, retval;

	in_irq = gkt_irq_context();
	timeout = in_irq ? GKT_IMMEDIATELY : 100;
	event.flags = 0;
	event.major_type = GKT_EVENT_SYSTEM;
	event.minor_type = minor_type;
	for (i = 0; i < GKT_EVENT_DATA_WORDS; i++)
		event.data[i] = data[i];
	retval = gkt_event_put(&event, timeout);
	if (retval != GKT_SUCCESS)
		window_system_pend_event(minor_type, data);
}
#endif

#if defined(GKT_CONFIG_DOOR_LOCK_ENABLE) && GKT_CONFIG_DOOR_LOCK_ENABLE
static void window_system_door_notify(uint32_t type, uint32_t status)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { type, status };

	window_system_put_event(GKT_WINDOW_SYSTEM_DOOR, data);
}
#endif
#if defined(GKT_CONFIG_FACTORY_RESET_ENABLE) && GKT_CONFIG_FACTORY_RESET_ENABLE \
	&& GKT_FACTORY_RESET_INDIVIDUAL_ENABLE
static void window_system_factory_reset_notify(int type, int detect)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { (uint32_t)type, (uint32_t)detect };
	window_system_put_event(GKT_WINDOW_SYSTEM_FACTORY_RESET, data);
}
#endif

#if defined(GKT_CONFIG_CAT_EYE_ENABLE) && GKT_CONFIG_CAT_EYE_ENABLE
static void window_system_cat_eye_notify(gkt_cat_eye_status_e status)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { 0, status };
	window_system_put_event(GKT_WINDOW_SYSTEM_CAT_EYE, data);
}
#endif

#if defined(GKT_CONFIG_INDOOR_BELL_ENABLE) && GKT_CONFIG_INDOOR_BELL_ENABLE
static void window_system_indoor_bell_notify(uint32_t status)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { status, 0 };
	window_system_put_event(GKT_WINDOW_SYSTEM_INDOOR_BELL, data);
}
#endif
#if defined(GKT_CONFIG_BACKBOARD_ENABLE) && GKT_CONFIG_BACKBOARD_ENABLE
static void window_system_backboard_common_notify(uint32_t type, uint32_t status)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { type, status };
	window_system_put_event(GKT_WINDOW_SYSTEM_BACKBOARD, data);
}
#endif
#if defined(GKT_CONFIG_NETWORK_ENABLE) && GKT_CONFIG_NETWORK_ENABLE
static void window_system_network_notify(uint32_t type, uint32_t status)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { type, status };
	window_system_put_event(GKT_WINDOW_SYSTEM_NETWORK, data);
}
#endif

#if GKT_SLIDING_CLOSURE_ENABLE

#define WINDOW_SYSTEM_SLIDING_CLOSURE_TIMER_ID	\
	(WINDOW_SYSTEM_TIMER_MASK | GKT_WINDOW_SYSTEM_SLIDING_CLOSURE)

#ifdef GKT_CONFIG_SLIDING_CLOSURE_DELAY_TIME
#define WINDOW_SYSTEM_SLIDING_CLOSURE_DELAY_TIME	\
	GKT_CONFIG_SLIDING_CLOSURE_DELAY_TIME
#else
#define WINDOW_SYSTEM_SLIDING_CLOSURE_DELAY_TIME	500
#endif

static void window_system_sliding_closure_notify(int detect)
{
	uint32_t data[GKT_EVENT_DATA_WORDS] = { detect, 0 };
	window_system_put_event(GKT_WINDOW_SYSTEM_SLIDING_CLOSURE, data);
}

static void window_system_sliding_closure_event_handler(gkt_event_s *event)
{
	int detect, retval;

	detect = gkt_sliding_closure_is_detected();
	gkt_info("window_system: SLIDING_CLOSURE - (%d-%d) detect(%u --> %u)\n", 
			s_window_system.standby_disable, s_window_system.standby_pending,
			s_window_system.sliding_closure_detect, detect);

	if (s_window_system.sliding_closure_detect != detect) {
		s_window_system.sliding_closure_detect = detect;

		if (!detect) {	
			/* mask rfid */
			s_window_system.card_is_masked = gkt_event_is_masked(GKT_EVENT_CARD);
			if (!s_window_system.card_is_masked)
				gkt_event_mask_single(GKT_EVENT_CARD, 0);

			if (!s_window_system.standby_disable) {
				gkt_key_led_control(GKT_KEY_LED_CTRL_ENABLE, 0);
#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE
				gkt_screen_control(GKT_SCREEN_CTRL_DISPLAY_OFF, 0);
#endif
			}
			else {
				s_window_system.standby_pending = 1;
				return;
			}

#if (WINDOW_SYSTEM_SLIDING_CLOSURE_DELAY_TIME > 0)
			s_window_system.sliding_closure_seq++;
			retval = window_system_start_timer(GKT_WINDOW_SYSTEM_SLIDING_CLOSURE,
						WINDOW_SYSTEM_SLIDING_CLOSURE_DELAY_TIME, 0,
						s_window_system.sliding_closure_seq);
			if (retval != GKT_SUCCESS)
				gkt_window_system_standby(0);
#else
			gkt_window_system_standby(0);
			gkt_unused(window_system_start_timer);
			gkt_unused(window_system_stop_timer);
#endif
		}
		else {
			s_window_system.standby_pending = 0;

			window_system_stop_timer(GKT_WINDOW_SYSTEM_SLIDING_CLOSURE);
			gkt_sliding_closure_magnet_control(1);

#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE
			gkt_screen_control(GKT_SCREEN_CTRL_DISPLAY_ON, 0);
#endif
			gkt_key_led_control(GKT_KEY_LED_CTRL_ENABLE, 1);

			if (!s_window_system.card_is_masked)
				gkt_event_unmask_single(GKT_EVENT_CARD);
		}
	}
}
#endif

int gkt_window_system_event_handler(gkt_event_s *event)
{
	gkt_window_s *window_active, *window_start;
	int retval;

	gkt_trace("window_system: %u - %u\n",  
		event->minor_type, event->data[0]);

	gkt_window_timeout_reset();

#if GKT_SLIDING_CLOSURE_ENABLE
	if (GKT_WINDOW_SYSTEM_SLIDING_CLOSURE == event->minor_type) {
		window_system_sliding_closure_event_handler(event);
		return GKT_EVENT_HANDLE_COMPLETE;
	}
#endif

	window_active = gkt_window_find(GKT_WINDOW_ID_ACTIVE);
	if (window_active) {
		retval = gkt_window_custom_event_handle(window_active,
						event->major_type, event->minor_type,
						event->data[0], event->data[1]);
		if (GKT_EVENT_HANDLE_COMPLETE == retval)
			return GKT_EVENT_HANDLE_COMPLETE;
	}

	window_start = gkt_window_find(GKT_WINDOW_ID_START);
	if (window_start && (window_start != window_active)) {
		gkt_window_custom_event_handle(window_start,
				event->major_type, event->minor_type,
				event->data[0], event->data[1]);
	}

	return GKT_EVENT_HANDLE_COMPLETE;
}

static void window_system_timer_handler(uint32_t type, uint32_t data)
{
#if GKT_SLIDING_CLOSURE_ENABLE
	if (GKT_WINDOW_SYSTEM_SLIDING_CLOSURE == type) {
		int sliding_closure_detect;

		sliding_closure_detect = gkt_sliding_closure_is_detected();
		if (!sliding_closure_detect
			&& !s_window_system.sliding_closure_detect
			&& (s_window_system.sliding_closure_seq == data))
		{
			window_system_stop_timer(GKT_WINDOW_SYSTEM_SLIDING_CLOSURE);
			gkt_window_system_standby(0);
		}
	}
#endif

	// TODO: other timer handles if needed
}

int gkt_window_system_is_standby_pending(void)
{
	return s_window_system.standby_pending;
}

int gkt_window_system_init(void)
{
	int retval=GKT_SUCCESS;

	gkt_window_timeout_init();
	gkt_window_timer_register_manage_handler(
			GKT_WINDOW_TIMER_MANAGE_SYSTEM,
			window_system_timer_handler);

	gkt_io_debounce_init();

#if GKT_SLIDING_CLOSURE_ENABLE
	/* sliding_closure */
	retval = gkt_sliding_closure_init();
	if (GKT_SUCCESS == retval) {
		s_window_system.sliding_closure_detect = gkt_sliding_closure_is_detected();
		gkt_sliding_closure_register_notify(window_system_sliding_closure_notify);
	}
	else
		gkt_error("window_system: sliding_closure init failed %d\n", retval);
#endif

#if defined(GKT_CONFIG_BACKBOARD_ENABLE) && GKT_CONFIG_BACKBOARD_ENABLE
	retval = gkt_backboard_common_init();
	if (GKT_SUCCESS == retval)
		gkt_backboard_common_register_notify(window_system_backboard_common_notify);
	else
		gkt_error("window_system: backboard common init failed %d\n", retval);
#endif

#if defined(GKT_CONFIG_DOOR_LOCK_ENABLE) && GKT_CONFIG_DOOR_LOCK_ENABLE 
	retval = gkt_door_init();
	if (GKT_SUCCESS == retval)
		gkt_door_register_notify(window_system_door_notify);
	else
		gkt_error("window_system: door init failed %d\n", retval);
#endif
	
#if GKT_FACTORY_RESET_INDIVIDUAL_ENABLE
	/* factory reset */
	retval = gkt_factory_reset_init();
	if (GKT_SUCCESS == retval)
		gkt_factory_reset_register_notify(window_system_factory_reset_notify);
	else
		gkt_error("window_system: factory_reset init failed %d\n", retval);
#endif
#if defined(GKT_CONFIG_VISUAL_INTERCOM_ENABLE) && GKT_CONFIG_VISUAL_INTERCOM_ENABLE
	retval = gkt_visual_intercom_init();
	if (GKT_SUCCESS != retval)
		gkt_error("window_system: visual intercom init failed %d\n", retval);
#endif
#if defined(GKT_CONFIG_NETWORK_ENABLE) && GKT_CONFIG_NETWORK_ENABLE
	retval = gkt_network_init();
	if (GKT_SUCCESS == retval)
		gkt_network_register_notify(window_system_network_notify);
	else
		gkt_error("window_system: network init failed %d\n", retval);
#endif

#if defined(GKT_CONFIG_CAT_EYE_ENABLE) && GKT_CONFIG_CAT_EYE_ENABLE
	retval = gkt_cat_eye_init();
	if (GKT_SUCCESS == retval)
		gkt_cat_eye_register_notify(window_system_cat_eye_notify);	
#endif

#if defined(GKT_CONFIG_INDOOR_BELL_ENABLE) && GKT_CONFIG_INDOOR_BELL_ENABLE
	retval = gkt_indoor_bell_init();
	if (GKT_SUCCESS == retval)
		gkt_indoor_bell_register_notify(window_system_indoor_bell_notify);
#endif

	return retval;
}

#define gkt_window_system_mask	NULL

GKT_EVENT_DESC_DEFINE(SYSTEM, window_system);

